package com.geeguo.ebuilder.client.business.system.user.service.impl;

import cn.idev.excel.FastExcel;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geeguo.ebuilder.client.base.core.constants.VirtualConstants;
import com.geeguo.ebuilder.client.base.core.dictionary.CertificateTypeEnum;
import com.geeguo.ebuilder.client.base.core.dictionary.DefaultRoleEnum;
import com.geeguo.ebuilder.client.base.core.dictionary.ExcelImportTypeEnum;
import com.geeguo.ebuilder.client.base.core.utils.FileHelper;
import com.geeguo.ebuilder.client.base.database.annotation.TDS;
import com.geeguo.ebuilder.client.base.redis.utils.CacheKeyFactory;
import com.geeguo.ebuilder.client.base.tenant.model.DataContext;
import com.geeguo.ebuilder.client.business.system.excelimport.model.ExcelImportVO;
import com.geeguo.ebuilder.client.business.system.excelimport.service.ExcelImportService;
import com.geeguo.ebuilder.client.business.system.excelimportfailitem.model.ExcelImportFailItemEntity;
import com.geeguo.ebuilder.client.business.system.excelimportfailitem.service.ExcelImportFailItemService;
import com.geeguo.ebuilder.client.business.system.logincertificate.model.LoginCertificateEntity;
import com.geeguo.ebuilder.client.business.system.logincertificate.service.LoginCertificateService;
import com.geeguo.ebuilder.client.business.system.organization.model.OrganizationCO;
import com.geeguo.ebuilder.client.business.system.organization.model.OrganizationEntity;
import com.geeguo.ebuilder.client.business.system.organization.service.OrganizationService;
import com.geeguo.ebuilder.client.business.system.role.model.RoleEntity;
import com.geeguo.ebuilder.client.business.system.role.model.RoleVO;
import com.geeguo.ebuilder.client.business.system.role.service.RoleService;
import com.geeguo.ebuilder.client.business.system.user.mapper.UserMapper;
import com.geeguo.ebuilder.client.business.system.user.model.*;
import com.geeguo.ebuilder.client.business.system.user.service.UserService;
import com.geeguo.ebuilder.client.business.system.userrole.model.UserRoleEntity;
import com.geeguo.ebuilder.client.business.system.userrole.service.UserRoleService;
import com.geeguo.ebuilder.core.common.dictionary.GenderEnum;
import com.geeguo.ebuilder.core.common.dictionary.StateEnum;
import com.geeguo.ebuilder.core.common.exception.ApplicationException;
import com.geeguo.ebuilder.core.common.model.vo.PageListVO;
import com.geeguo.ebuilder.core.common.utils.SqlHelper;
import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.ObjectEqualsHelper;
import com.geeguo.ebuilder.core.common.utils.PageUtils;
import com.geeguo.ebuilder.core.common.utils.key.UUIDGenerator;
import com.geeguo.ebuilder.core.redis.constants.CacheTimeConstants;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RedisCacheService redisCacheService;
    @Autowired
    private ExcelImportService excelImportService;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private LoginCertificateService loginCertificateService;
    @Autowired
    private ExcelImportFailItemService excelImportFailItemService;

    @Override
    public ExcelImportTypeEnum getExcelImportTypeEnum() {
        return ExcelImportTypeEnum.User;
    }

    @TDS
    @Override
    public PageListVO<UserVO> list(UserQuery query, DataContext dataContext) {
        Map<String, Object> params = new HashMap<>();
        if (StringUtils.isNotEmpty(query.getUsername())) {
            params.put("username", SqlHelper.getFullImplict(query.getUsername()));
        }
        if (StringUtils.isNotEmpty(query.getRealName())) {
            params.put("realName", SqlHelper.getFullImplict(query.getRealName()));
        }
        if (StringUtils.isNotEmpty(query.getOrgId())) {
            List<String> childrenIds = organizationService.listChildrenIds(query.getOrgId(), dataContext);
            params.put("orgIds", childrenIds);
        }
        if (query.getState() != null && query.getState() > 0) {
            params.put("state", query.getState());
        }
        params.put("tenantCode", dataContext.getTenantCode());
        IPage<UserEntity> page = userMapper.list(new Page<>(query.getPageNo(), query.getPageSize()), params);
        List<UserVO> userList = new ArrayList<>();
        if (page.getRecords() != null && !page.getRecords().isEmpty()) {
            for (UserEntity item : page.getRecords()) {
                UserVO vo = UserConverter.INSTANCE.convert2VO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId(), dataContext);
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                userList.add(vo);
            }
        }
        return PageUtils.getPageListVO(page, userList);
    }

    @TDS
    @Override
    public PageListVO<UserSimpleVO> listSimple(UserQuery query, DataContext dataContext) {
        Map<String, Object> params = new HashMap<>();
        if (StringUtils.isNotEmpty(query.getUsername())) {
            params.put("username", SqlHelper.getFullImplict(query.getUsername()));
        }
        if (StringUtils.isNotEmpty(query.getRealName())) {
            params.put("realName", SqlHelper.getFullImplict(query.getRealName()));
        }
        params.put("tenantCode", dataContext.getTenantCode());
        IPage<UserEntity> page = userMapper.listSimple(new Page<>(query.getPageNo(), query.getPageSize()), params);
        List<UserSimpleVO> userList = new ArrayList<>();
        if (page.getRecords() != null && !page.getRecords().isEmpty()) {
            for (UserEntity item : page.getRecords()) {
                UserSimpleVO vo = UserConverter.INSTANCE.convert2SimpleVO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId(), dataContext);
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                userList.add(vo);
            }
        }
        return PageUtils.getPageListVO(page, userList);
    }

    @TDS
    @Override
    public List<UserSimpleVO> search(String realName, DataContext dataContext) {
        List<UserSimpleVO> result = null;
        List<UserEntity> list = userMapper.search(SqlHelper.getFullImplict(realName), dataContext.getTenantCode());
        if (list != null && !list.isEmpty()) {
            result = new ArrayList<>();
            for (UserEntity item : list) {
                UserSimpleVO vo = UserConverter.INSTANCE.convert2SimpleVO(item);
                if (StringUtils.isNotEmpty(item.getOrgId())) {
                    OrganizationCO organization = organizationService.getCached(vo.getOrgId(), dataContext);
                    if (organization != null) {
                        vo.setOrgName(organization.getName());
                    }
                }
                result.add(vo);
            }
        }
        return result;
    }

    @TDS
    @Override
    public UserEntity get(String id, DataContext dataContext) {
        return userMapper.get(id);
    }

    @Override
    public UserCO getCached(String id, DataContext dataContext) {
        UserCO result = null;
        String cacheKey = CacheKeyFactory.INSTANCE.getUser(id, dataContext);
        String cacheJson = redisCacheService.get(cacheKey);
        if (StringUtils.isNotEmpty(cacheJson)) {
            result = GsonUtils.fromJson(cacheJson, UserCO.class);
        }
        if (result == null) {
            UserEntity entity = this.get(id, dataContext);
            if (entity != null) {
                result = UserConverter.INSTANCE.convert2CO(entity);
                cacheJson = GsonUtils.toJson(result);
                redisCacheService.set(cacheKey, cacheJson, CacheTimeConstants.CACHE_WEEK);
            }
        }
        return result;
    }

    @TDS
    @Override
    public UserEntity getByCertificate(String certificate, DataContext dataContext) {
        UserEntity user = null;
        LoginCertificateEntity loginCertificate = loginCertificateService.getByCertificate(certificate, dataContext);
        if (loginCertificate != null) {
            user = userMapper.get(loginCertificate.getUserId());
        }
        return user;
    }

    @TDS
    @Override
    @Transactional
    public boolean save(UserEntity user, List<String> roles, DataContext dataContext) {
        if (StringUtils.isBlank(user.getUsername())) {
            user.setUsername(null);
        }
        if (StringUtils.isBlank(user.getMobile())) {
            user.setMobile(null);
        }
        if (StringUtils.isBlank(user.getEmail())) {
            user.setEmail(null);
        }
        boolean result = userMapper.insert(user) > 0;
        if (result && StringUtils.isNotEmpty(user.getUsername())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getUsername());
            loginCertificate.setCertificateType(CertificateTypeEnum.Username.getValue());
            loginCertificate.setTenantCode(dataContext.getTenantCode());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate, dataContext);
        }
        if (result && StringUtils.isNotEmpty(user.getMobile())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getMobile());
            loginCertificate.setCertificateType(CertificateTypeEnum.Mobile.getValue());
            loginCertificate.setTenantCode(dataContext.getTenantCode());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate, dataContext);
        }
        if (result && StringUtils.isNotEmpty(user.getEmail())) {
            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
            loginCertificate.setId(UUIDGenerator.getId());
            loginCertificate.setUserId(user.getId());
            loginCertificate.setCertificate(user.getEmail());
            loginCertificate.setCertificateType(CertificateTypeEnum.Email.getValue());
            loginCertificate.setTenantCode(dataContext.getTenantCode());
            loginCertificate.setCreateTime(user.getCreateTime());
            loginCertificate.setCreateUser(user.getCreateUser());
            result = loginCertificateService.save(loginCertificate, dataContext);
        }
        if (result && CollectionUtils.isNotEmpty(roles)) {
            result = userRoleService.batchSave(roles, user.getId(), user.getCreateUser(), dataContext);
        }
        if (!result) {
            throw new ApplicationException("保存用户失败。");
        }
        return result;
    }

    @TDS
    @Override
    @Transactional
    public boolean batchSave(List<UserEntity> list, DataContext dataContext) {
        return super.saveBatch(list);
    }

    @TDS
    @Override
    public boolean update(UserEntity current, UserEntity old, List<String> roles, DataContext dataContext) {
        boolean result = userRoleService.batchSave(roles, current.getId(), current.getModifyUser(), dataContext);
        Map<String, Object> changeValues = ObjectEqualsHelper.equals(current, old, new String[] {"username", "realName",
                "gender", "mobile", "email", "orgId", "postId", "hireDate", "state"});
        if (changeValues.isEmpty()) {
            return result;
        } else {
            changeValues.put("id", current.getId());
            changeValues.put("modifyTime", current.getModifyTime());
            changeValues.put("modifyUser", current.getModifyUser());
            result = userMapper.updateByParams(changeValues) == 1;
            if (result) {
                removeCached(current.getId(), dataContext);
            }
        }
        if (!result) {
            throw new ApplicationException("更新用户失败。");
        }
        return true;
    }

    @TDS
    @Override
    public boolean updatePassword(String id, String password, Date modifyTime, String modifyUser, DataContext dataContext) {
        return userMapper.updatePassword(id, password, modifyTime, modifyUser) == 1;
    }

    @TDS
    @Override
    public boolean updateLastLogin(String id, DataContext dataContext) {
        LambdaUpdateWrapper<UserEntity> update = new LambdaUpdateWrapper<>();
        update.set(UserEntity::getLastLoginTime, new Date());
        update.eq(UserEntity::getId, id);
        return userMapper.update(update) == 1;
    }

    @TDS
    @Override
    @Transactional
    public boolean remove(String id, DataContext dataContext) {
        boolean result = userMapper.deleteById(id) == 1;
        if (result) {
            result = loginCertificateService.removeByUserId(id, dataContext);
        }
        if (result) {
            result = userRoleService.removeByUserId(id, dataContext);
        }
        if (result) {
            removeCached(id, dataContext);
        }
        if (!result) {
            throw new ApplicationException("删除用户失败。");
        }
        return result;
    }

    @TDS
    @Override
    public boolean existsUsername(String username, DataContext dataContext) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getUsername, username);
        query.eq(UserEntity::getTenantCode, dataContext.getTenantCode());
        return userMapper.selectCount(query) > 0;
    }

    @TDS
    @Override
    public boolean existsMobile(String mobile, DataContext dataContext) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getMobile, mobile);
        query.eq(UserEntity::getTenantCode, dataContext.getTenantCode());
        return userMapper.selectCount(query) > 0;
    }

    @TDS
    @Override
    public boolean existsEmail(String email, DataContext dataContext) {
        LambdaQueryWrapper<UserEntity> query = new LambdaQueryWrapper<>();
        query.eq(UserEntity::getEmail, email);
        query.eq(UserEntity::getTenantCode, dataContext.getTenantCode());
        return userMapper.selectCount(query) > 0;
    }

    @TDS
    @Override
    @Transactional
    public UserEntity initSuperAdmin(String username, String password, DataContext dataContext) {
        Date currentTime = new Date();
        String managerId = UUIDGenerator.getId();

        OrganizationEntity organization = new OrganizationEntity();
        organization.setId(UUIDGenerator.getId());
        organization.setParentId(VirtualConstants.PARENT_ID);
        organization.setCode("00001");
        organization.setName("默认组织");
        organization.setDepth(1);
        organization.setSequence(1);
        organization.setState(StateEnum.Valid.getValue());
        organization.setTenantCode(dataContext.getTenantCode());
        organization.setCreateTime(currentTime);
        organization.setCreateUser(managerId);
        organizationService.save(organization, dataContext);

        RoleEntity role = new RoleEntity();
        role.setId(UUIDGenerator.getId());
        role.setCode(DefaultRoleEnum.SuperAdmin.getCode());
        role.setName(DefaultRoleEnum.SuperAdmin.getLabel());
        role.setState(StateEnum.Valid.getValue());
        role.setTenantCode(dataContext.getTenantCode());
        role.setCreateTime(currentTime);
        role.setCreateUser(managerId);
        roleService.save(role, dataContext);

        UserEntity manager = new UserEntity();
        manager.setId(managerId);
        manager.setUsername(username);
        manager.setPassword(password);
        manager.setRealName("超级管理员");
        manager.setGender(GenderEnum.Male.getValue());
        manager.setOrgId(organization.getId());
        manager.setState(StateEnum.Valid.getValue());
        manager.setTenantCode(dataContext.getTenantCode());
        manager.setCreateTime(currentTime);
        manager.setCreateUser(managerId);
        save(manager, List.of(role.getId()), dataContext);

        return manager;
    }

    @TDS
    @Override
    public void executeImport(ExcelImportVO excelImport, DataContext dataContext) {
        if (excelImport != null && StringUtils.isNotEmpty(excelImport.getFilePath())) {
            String filePath = FileHelper.getFullFilePath(excelImport.getFilePath());
            FastExcel.read(filePath, UserEO.class, new ReadListener<UserEO>() {

                Date currentTime = new Date();
                List<UserEntity> userList = new ArrayList<>();
                List<UserRoleEntity> userRoleList = new ArrayList<>();
                List<LoginCertificateEntity> loginCertificateList = new ArrayList<>();
                List<ExcelImportFailItemEntity> failList = new ArrayList<>();

                @Override
                public void invoke(UserEO data, AnalysisContext context) {
                    if (StringUtils.isNotEmpty(data.getUsername())
                            && StringUtils.isNotEmpty(data.getPassword())
                            && StringUtils.isNotEmpty(data.getRealName())
                            && StringUtils.isNotEmpty(data.getOrgNames())
                            && StringUtils.isNotEmpty(data.getRoleName())) {

                        UserEntity entity = UserConverter.INSTANCE.convert2Entity(data);

                        // 判断用户名是否存在
                        if (loginCertificateService.getByCertificate(entity.getUsername(), dataContext) != null) {
                            addFailItem("用户名已存在", data);
                            return;
                        }

                        // 判断移动电话是否存在
                        if (StringUtils.isNotEmpty(entity.getMobile()) && loginCertificateService.getByCertificate(entity.getMobile(), dataContext) != null) {
                            addFailItem("移动电话已存在", data);
                            return;
                        }

                        // 判断电子邮件是否存在
                        if (StringUtils.isNotEmpty(entity.getEmail()) && loginCertificateService.getByCertificate(entity.getEmail(), dataContext) != null) {
                            addFailItem("电子邮件已存在", data);
                            return;
                        }

                        OrganizationEntity organization = organizationService.getByNames(data.getOrgNames(), dataContext);
                        if (organization == null) {
                            addFailItem("组织不存在", data);
                            return;
                        }

                        RoleVO role = roleService.getByName(data.getRoleName(), dataContext);
                        if (role == null) {
                            addFailItem("角色不存在", data);
                            return;
                        }

                        GenderEnum genderEnum = GenderEnum.parse(data.getGenderLabel());

                        entity.setId(UUIDGenerator.getId());
                        entity.setGender(genderEnum != null ? genderEnum.getValue() : null);
                        entity.setOrgId(organization.getId());
                        entity.setState(StateEnum.Valid.getValue());
                        entity.setTenantCode(dataContext.getTenantCode());
                        entity.setCreateTime(currentTime);
                        entity.setCreateUser(excelImport.getUserId());
                        userList.add(entity);

                        UserRoleEntity userRole = new UserRoleEntity();
                        userRole.setId(UUIDGenerator.getId());
                        userRole.setUserId(entity.getId());
                        userRole.setRoleId(role.getId());
                        userRole.setTenantCode(dataContext.getTenantCode());
                        userRole.setCreateTime(entity.getCreateTime());
                        userRole.setCreateUser(entity.getCreateUser());
                        userRoleList.add(userRole);

                        if (StringUtils.isNotEmpty(entity.getUsername())) {
                            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
                            loginCertificate.setId(UUIDGenerator.getId());
                            loginCertificate.setUserId(entity.getId());
                            loginCertificate.setCertificate(entity.getUsername());
                            loginCertificate.setCertificateType(CertificateTypeEnum.Username.getValue());
                            loginCertificate.setTenantCode(dataContext.getTenantCode());
                            loginCertificate.setCreateTime(entity.getCreateTime());
                            loginCertificate.setCreateUser(entity.getCreateUser());
                            loginCertificateList.add(loginCertificate);
                        }
                        if (StringUtils.isNotEmpty(entity.getMobile())) {
                            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
                            loginCertificate.setId(UUIDGenerator.getId());
                            loginCertificate.setUserId(entity.getId());
                            loginCertificate.setCertificate(entity.getMobile());
                            loginCertificate.setCertificateType(CertificateTypeEnum.Mobile.getValue());
                            loginCertificate.setTenantCode(dataContext.getTenantCode());
                            loginCertificate.setCreateTime(entity.getCreateTime());
                            loginCertificate.setCreateUser(entity.getCreateUser());
                            loginCertificateList.add(loginCertificate);
                        }
                        if (StringUtils.isNotEmpty(entity.getEmail())) {
                            LoginCertificateEntity loginCertificate = new LoginCertificateEntity();
                            loginCertificate.setId(UUIDGenerator.getId());
                            loginCertificate.setUserId(entity.getId());
                            loginCertificate.setCertificate(entity.getEmail());
                            loginCertificate.setCertificateType(CertificateTypeEnum.Email.getValue());
                            loginCertificate.setTenantCode(dataContext.getTenantCode());
                            loginCertificate.setCreateTime(entity.getCreateTime());
                            loginCertificate.setCreateUser(entity.getCreateUser());
                            loginCertificateList.add(loginCertificate);
                        }
                    } else {
                        addFailItem("数据填写不完整", data);
                    }
                }

                @Override
                @Transactional
                public void doAfterAllAnalysed(AnalysisContext context) {
                    if (userList != null && !userList.isEmpty()) {
                        batchSave(userList, dataContext);
                    }
                    if (userRoleList != null && !userRoleList.isEmpty()) {
                        userRoleService.batchSave(userRoleList, dataContext);
                    }
                    if (loginCertificateList != null && !loginCertificateList.isEmpty()) {
                        loginCertificateService.batchSave(loginCertificateList, dataContext);
                    }
                    if (failList != null && !failList.isEmpty()) {
                        excelImportFailItemService.batchSave(failList, dataContext);
                    }
                    excelImportService.updateCount(excelImport.getId(), userList.size(), failList.size(), dataContext);
                }

                private void addFailItem(String message, UserEO data) {
                    ExcelImportFailItemEntity failItem = new ExcelImportFailItemEntity();
                    failItem.setId(UUIDGenerator.getId());
                    failItem.setImportId(excelImport.getId());
                    failItem.setData(GsonUtils.toJson(data));
                    failItem.setMessage(message);
                    failItem.setCreateTime(currentTime);
                    failItem.setCreateUser(excelImport.getUserId());
                    failList.add(failItem);
                }
            }).sheet().doRead();
        }
    }

    private void removeCached(String id, DataContext dataContext) {
        String cacheKey = CacheKeyFactory.INSTANCE.getUser(id, dataContext);
        redisCacheService.del(cacheKey);
    }
}
